package org.example.server8001.service.impl;


import ch.ethz.ssh2.*;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.example.SystemDemo.comment.enity.HostInfo;
import org.example.SystemDemo.comment.req.ConnectREQ;
import org.example.server8001.mapper.InfoMapper;
import org.example.server8001.service.InfoService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;

@Service
public class InfoServiceImpl extends ServiceImpl<InfoMapper, HostInfo> implements InfoService {

    @Value("${server.ip}")
    private String serverIp;

    @Value("${server.port}")
    private Long serverPort;

    private static final Logger log = LoggerFactory.getLogger(InfoServiceImpl.class);
    private static String DEFAULTCHART = "UTF-8";
    private static final String toPath = "/var/linux-dash/";

    @Autowired
    private InfoMapper infoMapper;

    @Override
    public boolean Connect(ConnectREQ req) {
        long index_id = IdWorker.getId();
        String res = "";
        // 1. 建立远程连接
        InfoServiceImpl infoServiceImpl = new InfoServiceImpl();
        Connection conn = InfoServiceImpl.ConnectLinux(req);
        if (conn == null) {
            // 登录失败！
            return false;
        }
        // 1.5 创建目录
        InfoServiceImpl.execute(InfoServiceImpl.ConnectLinux(req), "mkdir " + toPath);

        // 1.55 获取目标服务器名称
        final String hostname = InfoServiceImpl.execute(InfoServiceImpl.ConnectLinux(req), "hostname");

//        // 2. 是否有java环境，没有则进行安装
//        if (StringUtils.isBlank(infoServiceImpl.execute(conn,"javac"))) {
//            conn = infoServiceImpl.ConnectLinux(req);
//
//        }

        // 3. 远程发送jar包并运行
        if (InfoServiceImpl.uoloadFile(InfoServiceImpl.ConnectLinux(req))) {
            int i = 8000;
            while (i < 8111) {
                if (StringUtils.isBlank(InfoServiceImpl.execute(InfoServiceImpl.ConnectLinux(req),"lsof -i:" + i))) {
                    break;
                }
                i++;
            }
            // 在 8000 - 9599 端口中都被使用了
            if (i > 9599) {
                return false;
            }
            // 运行jar包,后台运行模式，没有返回值
            final String command = new StringBuffer()
                    .append("nohup java -jar /var/linux-dash/linux-dash.jar")
                    .append(" --server.port=" + i)
                    .append(" --run-index=" + index_id)
//                    .append(" --server-ip=" + server_ip)
                    .append(String.format(" --server-ip=%s:%s", serverIp,serverPort))
                    .append(" > linux-dash.log 2>&1 &")
                    .toString();
            System.out.println("远程执行的命令是: " + command );
            InfoServiceImpl.execute(InfoServiceImpl.ConnectLinux(req),command);
            // 验证该程序端口是否正在运行
            if (!StringUtils.isBlank(InfoServiceImpl.execute(InfoServiceImpl.ConnectLinux(req),"lsof -i:" + i))){
                return false;
            }
            req.setRunPort(String.valueOf(i));
        } else {
            // 未找到文件或上传失败
            return false;
        }

//        System.err.println("运行成功");
        final HostInfo hostInfo = new HostInfo(req, index_id , hostname.replace("\n",""));
//        System.out.println(JSONUtil.stringify(hostInfo));
        // 4. 存储机器信息到数据库
        return this.save(hostInfo);
    }



    /**
     * 远程连接linux主机
     *
     * @return 登录成功返回true, 否则返回false
     */
    public static Connection ConnectLinux(ConnectREQ req) {
        boolean flag = false;
        Connection conn = null;
        try {
            conn = new Connection(req.getHostIp(),Integer.parseInt(req.getHostPort()));
            conn.connect(); // 连接
            flag = conn.authenticateWithPassword(req.getHostName(), req.getHostPass());  //认证
            if (flag) {
                log.info("========= 登录成功 =========");
                return conn;
            }
        } catch (IOException e) {
            log.error("========= 登录失败 =========");
            e.printStackTrace();
        }
        return conn;
    }

    /**
     * 远程执行 shell 脚本或命令
     *
     * @param cmd 即将执行的命令
     * @return 命令执行完毕后返回的结果值
     */
    public static String execute(Connection conn, String cmd) {
        String res = "";
        try {
            if (conn != null) {
                Session session = conn.openSession();   // 打开一个会话
                session.execCommand(cmd);   // 执行命令
                res = processStdout(session.getStdout(), DEFAULTCHART);
                // 如果为空，说明执行出错
                if (StringUtils.isBlank(res)) {
                    log.info("得到标准输出为空，链接conn:" + conn + ",执行的命令为：" + cmd);
                    log.info(processStdout(session.getStderr(), DEFAULTCHART));
                } else {
                    log.info("执行命令成功，链接conn：" + conn + ",执行命令: " + cmd);
                }
                conn.close();
                session.close();
            }
        } catch (IOException e) {
            log.info("执行命令失败,链接conn:" + conn + ",执行命令：" + cmd + "   " + e.getMessage());
            e.printStackTrace();
        }
        return res;
    }

    /**
     * 解析脚本执行返回的结果集
     *
     * @param in      输入流对象
     * @param charset 编码
     * @return 以纯文本的格式返回
     */
    private static String processStdout(InputStream in, String charset) {
        InputStream stdout = new StreamGobbler(in);
        StringBuffer buffer = new StringBuffer();
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(stdout, charset));
            String line = null;
            while ((line = br.readLine()) != null) {
                buffer.append(line + "\n");
            }
        } catch (UnsupportedEncodingException e) {
            log.error("解析脚本出错:" + e.getMessage());
            e.printStackTrace();
        } catch (IOException e) {
            log.error("解析脚本出错:" + e.getMessage());
            e.printStackTrace();
        }
        return buffer.toString();
    }


    /**
     * 上传文件到远程机器
     * @param conn 连接
     * @return 返回是否传输成功
     */
    public static boolean uoloadFile(Connection conn) {
        SCPOutputStream os = null;
        String cmd = "";
        try {
            if (conn != null) {
                Resource resource = new ClassPathResource("Client8000-1.0-SNAPSHOT.jar");
                MultipartFile multipartFile = new MockMultipartFile("test.jar",resource.getInputStream());
                String fn = multipartFile.getOriginalFilename();
                SCPClient scpClient = new SCPClient(conn);
                byte[] b = multipartFile.getBytes();
                os = scpClient.put("linux-dash.jar",b.length,toPath,null);
                os.write(b,0,b.length);
                Session session = conn.openSession();
                cmd = "chmod +r " + toPath + new File(fn).getName();
                log.info("linux命令 == " + cmd);
                session.execCommand(cmd);
                os.flush();
                os.close();
                conn.close();
            }
        }catch (Exception e) {
            log.error("出现错误!linux命令 == " + cmd + " ,error == " + e.getMessage());
            return false;
        }
        return true;

    }
}
